# comun self hosted implementation: comun all-in-one utility
#
# This is a general command line utility for the comun language, including
# compiler, optimizer, interpreter, debugger etc.
#
#  {}{}{} {}{}{} {}{}{} {}  {} {}{}{}
#  {}     {}  {} {}{}{} {}  {} {}  {}
#  {}{}{} {}{}{} {}  {} {}{}{} {}  {}

~v1                  # helper var
~v2                  # helper var 2
~p1:0                # helper ptr
~argp:0              # ptr to end of arguments
~flags               # CLI flags as bits
~sourceLine          # counts source code lines
~sourceLatestChars:6 # buffer to keep lastest chars read 
~state

flagToBit:
  $:v1
  0 "gdcbvtoh"
  $v1

  1 $:v1

  @@
    $1 !! $1 $3 = | ?
      !@
    .

    $v1 1 |< $:v1
    $:1
  .

  @'
    ^
  .

  ^ 
  $v1
.

printPrefixStr: "=== " .

# arg: 3num 3base 3digits
_numPrint:
  0

  ~32
  @'
    ~0 0 ~32
    $2 $2 % $0 10 < "0" "A" 10 - ?? + >0
    $2 $2 / $:3
    --
  .
  ^ ^ ^
  ~0

  -->
.

numPrintD6: ~32 0 ~0 >32 ~32 10 6 ~0 _numPrint .
numPrintB8: ~32 0 ~0 >32 ~32 2 8 ~0  _numPrint .

isFlagSet: flagToBit $flags & .

verbosePrint:
  "v" isFlagSet ?
    -->
  ;
    @ .
  .
.

CMN_iIn: <? ? <- ; -1 . .
CMN_iOut: -> .

CMN_iGetArgs: #0 "abc" 0 "defgh" 2 
  $argp>p1

  $argp # pushes arg count
  @'
    1 @
      $<p1
      $p1
    .
 
    --
  .
  ^

  1 @
    $p1
    $>p1
    $p1=argp 1 !=
  .
.

CMN_iCallExt: 0 "EXT: " --> "0" + -> 10 -> .

CMN_cInclude:
  0 34 "include: " printPrefixStr verbosePrint
  verbosePrint # filename string
  0 10 34 verbosePrint
.

~32 CMN_iMEM_SIZE: 8192 . ~0
~32 ~CMN_iMem:8192 ~0

~32 CMN_cMEM_SIZE: 2048 . ~0
~32 ~CMN_cMem:2048 ~0

~32 CMN_bBYTECODE_SIZE: 32768 . ~0
~16 ~CMN_bBytecode:32768 ~0

~"cmn_all.cmn"

printHelp:
  0
  10 "  g insert debug info to bytecode"
  10 "  c compile, output bytecode (else interpret)"
  10 "  d debug"
  10 "flags (first '-' starting arg, as a single string):" 
  10 CMN_gLANG_V_STR "lang. ver: "
  10 "comun: all-in-one utility"
  -->

  0
  10 "  remaining args go to interpreted program"
  10 "  b input bytecode (else input source code)"
  10 "  v verbose"
  10 "  t compile, output text (else interpret)"
  10 "  o optimize"
  10 "  h help"
  -->

  # TODO: flag to analyze program (estimate, measure, ...)
  # TODO: optimization levels (o vs O?)
.

printStackTops:
  0 "stack tops: " printPrefixStr -->
  $$ numPrintD6 " " ->
  0 ~32 $$ >0 ~0 numPrintD6 " " ->
  0 ~16 $$ >0 ~0 numPrintD6 " " ->
  0 ~8  $$ >0 ~0 numPrintD6 " " ->
  10 ->
.

printCompiler:
  0 10 "compiler" printPrefixStr -->
  0 "state = " -->        $CMN_cState numPrintD6
  0 "; addr. size = " --> _CMN_cDefaultAddrSize numPrintD6

  10 ->

  0 10 "  symbol table: " -->

  0 _CMN_cSymbolTableSize ~32 >0 ~0
  @'
    --
    0 "    " --> 0 $1 CMN_cSymbolGet --> " " -> 0 ~32 >0 ~0 numPrintD6 10 ->
  .
  ^

  # TODO

  10 ->
.

# Prints BC in CMN_bBytecode to output.
printBytecode:
  ~32 0 CMN_bCurrentInstrSetAddr ~0

  0 $:v1

  1 @
    CMN_bCurrentInstrGet
    CMN_bCurrentInstrShift

    $v1 8 |> numPrintB8 $v1 numPrintB8 ":" -> " " ->

    $0 CMN_eInstrToStr --> 

    $0 CMN_bInstrOpcode CMN_bOPCODE_DES = ?
      0 $:v2 # potential extra number to print

      $0 8 |> +x0f &

      $0 CMN_bDES_IF = ?      0 " # if"      ;
      $0 CMN_bDES_LOOP = ?    0 " # loop"    ;
      $0 CMN_bDES_ELSE = ?    0 " # else"    ;
      $0 CMN_bDES_ENDIF = ?   0 " # endif"   ;
      $0 CMN_bDES_ENDLOOP = ? 0 " # endloop" ;
      $0 CMN_bDES_BREAK = ?   0 " # break"   ;
      $0 CMN_bDES_EXIT = ?    0 " # exit"    ;
      $0 CMN_bDES_FUNDEF = ?  0 " # func"    ;
      $0 CMN_bDES_LABEL =  ?  0 " # label"   ;
      $0 CMN_bDES_GOTO =  ?   0 " # goto"    ;
      $0 CMN_bDES_NEWLINE = ? 0 " # newline " CMN_bCurrentInstrGetSrcLine $:v2 ;
      $0 CMN_bDES_NEWFILE = ? 0 " # newfile" ;
      $0 CMN_bDES_ENDFILE = ? 0 " # endfile" ;
                              0 # TODO: these zeros could be all moved outside
      . . . . . . . . . . . . .

      -->

      $v2 ?
        $v2 numPrintD6
      .

      ^
    .

    10 ->

    $v1 ++ $:v1
  .
.

readSourceChar:
  <-

  $0 10 = ?
    $sourceLine ++ $:sourceLine
  .

  $sourceLatestChars>p1

  5 @'
    $>p1 
    $p1 $p1 10 != >< " " ??      
    $<p1 $:p1 $>p1
    --
  .
  ^

  $0 $:p1
.

compile:
  0 10 "compiling" printPrefixStr verbosePrint

  0 CMN_cInit

  1 @
    readSourceChar
    $0 $:v1

    CMN_cFeedChar

    $v1 10 = ?
      "g" isFlagSet ? # nested branch to not check on every single char
        CMN_cInsertDebugNewLine
      .
    .

    $CMN_cState CMN_cSTATE_OK != <? !! | !!
  .

  $CMN_cState CMN_cSTATE_DONE = ?
    0 10 "compiled successfuly" printPrefixStr verbosePrint
    1 # state
  ;
    0 $:state

    0 "line "

    $CMN_cState CMN_cSTATE_ERROR_TOKEN = ?       "(bad token) "        ;
    $CMN_cState CMN_cSTATE_ERROR_UNEXPECTED = ?  "(unexpected) "       ;
    $CMN_cState CMN_cSTATE_ERROR_NAME_UNK = ?    "(unknown name) "     ;
    $CMN_cState CMN_cSTATE_ERROR_NAME_EXISTS = ? "(name redefined) "   ;
    $CMN_cState CMN_cSTATE_ERROR_SYMBOL_TAB = ?  "(symb. tab. mem.) "  ;
    $CMN_cState CMN_cSTATE_ERROR_BC_TOO_BIG = ?  "(bytecode too big) "
    . . . . . .

    " " $CMN_cState 2 - "0" + "ERROR "
    -->

    $sourceLine numPrintD6

    34 ": " -> -> ->

    $sourceLatestChars>p1

    6 @'
      $p1 -> $>p1
      --
    .
    ^

    34 -> 10 ->

    0 10 "couldn't compile" printPrefixStr verbosePrint
    0 # state
  .
.

interpret:
  0 10 "interpreting" printPrefixStr verbosePrint

  32 CMN_iInit

  1 @
    CMN_iStep
    $CMN_iState CMN_iSTATE_RUNNING =
  .

  $CMN_iState CMN_iSTATE_DONE = ?
    0 10 "interpreting ended successfully" printPrefixStr verbosePrint
  ;
    0

    $CMN_iState CMN_iSTATE_ERROR_MEM = ?   " (memory access)" ;
    $CMN_iState CMN_iSTATE_ERROR_CALL = ?  " (call stack)" ;
    $CMN_iState CMN_iSTATE_ERROR_OP = ?    " (bad operation)" ;
    $CMN_iState CMN_iSTATE_ERROR_BC = ?    " (bad bytecode)"
    . . . .

    $CMN_iState 2 - "0" + "ERROR "
    -->

    0 10 "stack trace:" 10 -->

    CMN_iStackTrace

    @'
      "g" isFlagSet ?
        ~32 $CMN_bCurrentInstrAddr $1 CMN_bCurrentInstrSetAddr ~0
        0 "line " --> CMN_bCurrentInstrGetSrcLine numPrintD6 "," -> " " ->
        ~32 CMN_bCurrentInstrSetAddr ~0
      .

      0 "instr " -->  0 ~32 >0 ~0 numPrintD6 10 ->

      --
    .
    ^

    0 10 "interpreting ended with error" printPrefixStr verbosePrint
  .
.

$:v1 # argument count

@@ # read arguments
  $v1 0 = ?
    !@
  .

  $v1 -- $:v1

  "-" = ?
    @'
      $v1 ><
      flagToBit $flags | $:flags
      $:v1
    .

    ^
    !@ # comp args read, leave rest for later (in argp)
  ;
    @'
      ^
    .
  .

  ^
.

$v1
$0>argp

"h" isFlagSet ?
  printHelp
  !.
.

0 10 "starting" printPrefixStr verbosePrint

"d" isFlagSet ?
  printStackTops
.

"b" isFlagSet ?
  0 10 "loading bytecode" printPrefixStr verbosePrint
  CMN_eLoadBC

  CMN_bCheckSanity !! ?
    0 10 "ERROR: bad bytecode" -->
    !.
  .
;
  compile

  "d" isFlagSet ?
    printCompiler
  .
.

?
  "d" isFlagSet ?
    printStackTops
  .

  "o" isFlagSet ?
    2 CMN_oOptimize # TODO: 2 is magic constant here :)
  .

  "t" isFlagSet ?
    printBytecode
  ;
  "c" isFlagSet ?
    CMN_bOutput
  ;
    interpret
  . .
;
  0 10 "error, stopping" printPrefixStr verbosePrint
.

"d" isFlagSet ?
  printStackTops
.

0 10 "ending" printPrefixStr verbosePrint
